SPDX-FileCopyrightText: 2019 Arthur Schweisthal SPDX-FileCopyrightText: 2024 AlICe laboratory https://alicelab.be
SPDX-License-Identifier: GPL-3.0-or-later
Arthur_SCHWEISTHAL Blender 2.81
import bpy
import bmesh
import random
import math
from math import radians
from math import cos
from math import sinPARAMETERS
for now, the values must be equal if not, no rotation would be applied
length = 10
depth = 10
height = 10
new_thickness = 0.75SETTINGS
verts = [
    (+depth, +length, 0),  # 0
    (+depth, 0, 0),  # 1
    (0, 0, 0),  # 2
    (0, +length, 0),  # 3
    (+depth, +length, +height),  # 4
    (+depth, 0, +height),  # 5
    (0, 0, +height),  # 6
    (0, +length, +height),
]  # 7
faces = [
    (0, 1, 2, 3),  # 0 bottom
    (4, 7, 6, 5),  # 1 top
    (0, 4, 5, 1),  # 2 front
    (1, 5, 6, 2),  # 3 left
    (2, 6, 7, 3),  # 4 back
    (4, 0, 3, 7),  # 5 right
]                  GEOMETRY                    #
    MAKING AN AXONOMETRIC 3D GEOMETRY
Creating a mesh
def make_mesh(verts, faces, name):add new mesh in data
    mesh = bpy.data.meshes.new(name)use bmesh to generate mesh
    bm = bmesh.new()create bmesh vertex objects from coordinates
    for v_co in verts:
        bm.verts.new(v_co)create bmesh faces from collection of previous vertexes
    bm.verts.ensure_lookup_table()
    for f_idx in faces:
        bm.faces.new([bm.verts[i] for i in f_idx])put bmesh into new mesh in data
    bm.to_mesh(mesh)
    mesh.update()add the mesh as an object into the scene with this utility module
    from bpy_extras import object_utils
    object_utils.object_data_add(bpy.context, mesh)Making a cube
def make_cube(name="CUBE"):
    make_mesh(verts, faces, name)Define it by its edges
def cube_edges(name="CUBE_EDGES"):
    bpy.ops.object.editmode_toggle()  # on
    obj = bpy.context.edit_object
    me = obj.data
    bme = bmesh.from_edit_mesh(me)
    for i in range(6):
        bme.faces.ensure_lookup_table()select in face index
        bme.faces[(i)].select = Trueuse inset tool to create an inner square
        bpy.ops.mesh.inset(thickness=new_thickness)extrude the inner square
        bpy.ops.mesh.extrude_region_move(
            TRANSFORM_OT_translate={
                "value": (0, 0, -new_thickness),
                "orient_type": "NORMAL",
            }
        )delete the inner square to make a hole
        bpy.ops.mesh.delete(type="FACE")Shear tool
def shear():
    bpy.ops.mesh.select_all(action="SELECT")
    bpy.ops.transform.shear(
        value=1,
        orient_axis="X",
        orient_axis_ortho="Y",
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
        release_confirm=True,
    )
    bpy.ops.transform.shear(
        value=1,
        orient_axis="Y",
        orient_axis_ortho="X",
        orient_type="GLOBAL",
        orient_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1)),
        orient_matrix_type="GLOBAL",
        mirror=True,
        use_proportional_edit=False,
        proportional_edit_falloff="SMOOTH",
        proportional_size=1,
        use_proportional_connected=False,
        use_proportional_projected=False,
        release_confirm=True,
    )replace it as the previous tool moved the origin of the geometry
    bpy.ops.transform.translate(value=(-depth / 2, length / 2, 0))
    bpy.ops.object.editmode_toggle()  # off              RELATIONSHIP                    #
    RANDOMIZE RELATIONSHIP BETWEEN THE GEOMETRIES
Random number of 90° rotations in every axis
z = random.randint(1, 3)
y = random.randint(1, 3)
x = random.randint(1, 3)is it possible to rotate it randomly if the geometry is not a cube?
if length!=depth and height!=depth and length==height:
if length==depth and height!=depth and length!=height:
if length!=depth and height==depth and length!=height:
if length!=depth and height!=depth and length!=height:
if not, only translate the cube
def rotations():
    if depth == length and length == height and depth == height:
        for i in range(z):
            bpy.ops.transform.rotate(value=1.5708, orient_axis="Z")
        for i in range(y):
            bpy.ops.transform.rotate(value=1.5708, orient_axis="Y")
        for i in range(x):
            bpy.ops.transform.rotate(value=1.5708, orient_axis="X")HELPER: rotation data #
def printing_rotation_data():  #    print(" ")  #
    print("start")  #
    print(" ")  #
    print("Number of X axis 90 degres rotations =", x)  #
    print("Number of Y axis 90 degres rotations =", y)  #
    print("Number of Z axis 90 degres rotations =", z)  #Random translation of half units in every axis
r1 = random.randint(-2, 2)
r2 = random.randint(-2, 2)
r3 = random.randint(-2, 2)def snap():
    for i in range(r1):
        bpy.ops.transform.translate(value=(depth / 2, 0, 0))
    for i in range(r2):
        bpy.ops.transform.translate(value=(0, length / 2, 0))
    for i in range(r3):
        bpy.ops.transform.translate(value=(0, 0, height / 2))HELPER: translation data #
def printing_translation_data():  #    print("Translation on X axis=", r1)  #
    print("Translation on Y axis=", r2)  #
    print("Translation on Z axis=", r3)  #        UTILITY  AND  MAIN FONCTIONS          #
    UTILITY FONCTIONS
def reset():Cleaning datafile through saving and reopening
    save = bpy.ops.wm.save_mainfile
    open = bpy.ops.wm.open_mainfile
    path = bpy.data.filepathtest if file is previously saved
    if path is "":
        save()
        path = bpy.data.filepath
        print(".blend file saved in home directory")save end reopen file
    save(filepath=path)
    open(filepath=path)def deleteallobjects():Delete scene before running script
    select = bpy.ops.object.select_all
    delete = bpy.ops.object.deleteselect and delete all the objects
    select(action="SELECT")
    delete(use_global=False)MAIN FONCTIONS
CREATE A FIRST GEOMETRY
def geometry():
    make_cube("Cube")
    cube_edges()
    shear()
    bpy.ops.object.origin_set(type="ORIGIN_CENTER_OF_MASS")CREATE A SECOND GEOMETRY
def geometry2():
    geometry()
    rotations()
    snap()def main_program():delete all objects
    deleteallobjects()create the object
    geometry()
    geometry2()
    bpy.ops.object.select_all(action="SELECT")
    bpy.ops.object.join()PRINTING THE RANDOM VALUES USED
def printing_data():
    printing_rotation_data()
    print(" ")
    print("and")
    print(" ")
    printing_translation_data()
    print(" ")
    print("end")
    print(" ")
    print(50 * "*")                EXECUTION                     #
    main_program()
printing_data()              REPRESENTATION                  #
    CAMERA SETTINGS modified version of 18.11.2016 camera settings code by AlICe.lab
def PersCam(name):
    bpy.ops.object.camera_add()
    Scene = bpy.context.scene.render
    PersCam = bpy.context.object
    PersCam.data.type = "PERSP"
    PersCam.data.ortho_scale = 500
    PersCam.rotation_euler = (radians(45), 0, radians(45))
    PersCam.location = (25.0, -20.0, 35.0)
    Scene.pixel_aspect_x = 1def axoCam(canon):
    bpy.ops.object.camera_add()
    Scene = bpy.context.scene.render
    AxoCam = bpy.context.object
    AxoCam.data.type = "ORTHO"
    AxoCam.data.ortho_scale = 50
    if canon == "isometrie":
        AxoCam.name = "axoIsometrie"
        AxoCam.rotation_euler = (radians(54.74), 0.0, radians(45))
        AxoCam.location = (10.0, -10.0, 10.0)
        Scene.pixel_aspect_x = 1
    if canon == "dimetrie":
        AxoCam.name = "axoDimetrie"
        AxoCam.rotation_euler = (radians(60), 0.0, radians(23))
        AxoCam.location = (5.53, -13.04, 8.18)
        Scene.pixel_aspect_x = 1
    if canon == "trimetrie":
        AxoCam.name = "axoTrimetrie"
        AxoCam.rotation_euler = (radians(67), 0.0, radians(34))
        AxoCam.location = (8.59, -12.734, 6.52)
        Scene.pixel_aspect_x = 1MANUAL camera choice
PersCam(‘Pers1’)
axoCam("isometrie")axoCam (‘dimetrie’) axoCam (‘trimetrie’)
AUTOMATIC camera choice To develop
purge the blender file
reset()